#include "Sorter.h"
#include "SelectionSort.h"
#include "QuickSort.h"
#include "MergeSort.h"
#include <algorithm>//Para usar "copy".
#include <utility>  //Para usar "pair".
#include <fstream>  //Para ler/escrever arquivos.
#include <cstdlib>  //Para usar "atoi"
#include <string>
#include <list>
#include <vector>
#include <iostream>
using namespace std;

pair<int*,size_t> ler(const char *arquivo_nome)
{
    // Array de retorno:
    pair<int*,size_t> array(NULL,0);
    // Abrir o arquivo para leitura:
    ifstream arquivo(arquivo_nome);
    // Checar se é possível abrir o arquivo:
    if(arquivo.is_open())
    {
        list<int> valores_lidos;
        string linha;
        while(arquivo.good())
        {
            // Pegar uma linha do arquivo:
            getline(arquivo,linha);
            // Converter para inteiro e colocar na lista:
            if (!linha.empty()) valores_lidos.push_back((int)atol(linha.c_str()));
        }
        linha.clear();
        arquivo.close();
        // Gerar um array com o conteúdo da lista "valores_lidos":
        if (valores_lidos.size())
        {
            array.first = new (nothrow) int[valores_lidos.size()];
            if (array.first != NULL)
            {
                array.second = valores_lidos.size();
                size_t i=0;
                while (valores_lidos.size())
                {
                    array.first[i] = valores_lidos.front();
                    valores_lidos.pop_front();
                    i++;
                }
            }
        }
    }
    return(array);
}

// Função de comparação:
int comparar_inteiros(const void* a, const void* b)
{
    return(*(int*)a - *(int*)b);
}

// Função de impressão:
void imprimir(const int v[], size_t n)
{
    for (size_t i=0; i<n; i++) cout<<v[i]<<"\t";
    cout<<endl;
}

// Checar se um vetor está ordenado:
bool ordenado(const int v[], size_t n)
{
    for (size_t i=1; i<n; i++) if (v[i-1] > v[i]) return(false);
    return(true);
}

// Função de cópia:
void copiar(const int origem[], int* destino, size_t tamanho)
{
    if ((origem != NULL) && (destino != NULL) && (tamanho != 0)) copy(origem,origem+tamanho,destino);
}

int main()
{
    // Ler alguns inteiros de um arquivo de texto:
    pair<int*,size_t> inteiros = ler("Exemplos/inteiros_10000.txt");
    if (inteiros.first == NULL) return(1);

    // Criar uma cópia do vetor a ordenar:
    int* inteiros_copia = new (nothrow) int[inteiros.second];
    if (inteiros_copia == NULL)
    {
        delete[] inteiros.first;
        return(1);
    }

    // Vetor com o par<"nome","algoritmo"> para cada algoritmo de ordenação:
    vector< pair<const char*,Sorter*> > ordenadores;
    ordenadores.reserve(3);
    ordenadores.push_back(make_pair("SelectionSort",new SelectionSort(inteiros_copia,inteiros.second,sizeof(int),comparar_inteiros)));
    ordenadores.push_back(make_pair("QuickSort",    new QuickSort(inteiros_copia,inteiros.second,sizeof(int),comparar_inteiros)));
    ordenadores.push_back(make_pair("MergeSort",    new MergeSort(inteiros_copia,inteiros.second,sizeof(int),comparar_inteiros)));

    // Imprimir o vetor a ser ordenado:
    cout<<"* Vetor ("<<inteiros.second<<" elementos)"<<endl;
    //imprimir(inteiros.first,inteiros.second);

    // Ordenar o vetor com cada algoritmo de ordenação:
    cout<<endl;
    for (vector< pair<const char*,Sorter*> >::iterator p=ordenadores.begin() ; p<ordenadores.end() ; p++)
    {
        copiar(inteiros.first,inteiros_copia,inteiros.second);
        cout<<"* Nome: "<<(*p).first<<endl;
        cout<<"  + Tempo gasto: "<<(*p).second->ordenar()<<" ms."<<endl;
        cout<<"  + Ordenado? "; if (ordenado(inteiros_copia,inteiros.second)) cout<<"sim."<<endl; else cout<<"NAO!!!"<<endl;
        cout<<endl;
    }

    // Apagar o vetor com os algoritmos de ordenação:
    while (ordenadores.size())
    {
        delete ordenadores.back().second;
        ordenadores.pop_back();
    }

    // Apagar vetores:
    delete[] inteiros.first;
    delete[] inteiros_copia;

    return(0);
}
